"""
一对多关系基类，用于定义一对多关系的数据库表和 树结构Python 对象之间的映射。
这个类提供了基础结构，用于递归处理多层父表和子表的增删改查基本操作。
"""
from __future__ import annotations

import abc
from typing import Type, Optional
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, UniqueConstraint
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from sqlalchemy.orm import relationship, sessionmaker, Session, declarative_base

# from apaf_analysis.cae_parameter_analysis_models import Base

# 创建数据库引擎
# engine = create_engine('sqlite:///example.db', echo=True)
Base = declarative_base()


# Session = sessionmaker(bind=engine)


# 定义父类
class ParentTable(Base):
    __tablename__ = 'parent_table'

    id = Column(Integer, primary_key=True)
    name = Column(String, unique=True)
    children = relationship("ChildrenTable", back_populates="parent", cascade="all, delete, delete-orphan")


# 定义子类
class ChildrenTable(Base):
    __tablename__ = 'children_table'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    parent_id = Column(Integer, ForeignKey('parent_table.id'))
    # 添加组合唯一性约束
    __table_args__ = (UniqueConstraint('name', 'parent_id', name='uq_name_parent_id'),)
    parent = relationship("ParentTable", back_populates="children")
    grandchildren = relationship("GrandChildrenTable", back_populates="child", cascade="all, delete, delete-orphan")


# 定义孙子类
class GrandChildrenTable(Base):
    __tablename__ = 'grandchildren_table'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    child_id = Column(Integer, ForeignKey('children_table.id'))
    # 添加组合唯一性约束
    __table_args__ = (UniqueConstraint('name', 'child_id', name='uq_name_child_id'),)
    child = relationship("ChildrenTable", back_populates="grandchildren")


class OneToManyBase:
    """
    一对多关系基类，用于定义一对多关系的数据库表和 树结构Python 对象之间的映射。
    这个类提供了基础结构，用于递归处理多层父表和子表的增删改查基本操作。
    """
    # 定义当前表的类类型
    CURRENT_TABLE_CLASS: Type[Base] = None
    # 定义子表的类类型
    CHILDREN_TABLE_CLASS: Type[Base] = None
    # 定义子数据的类类型，这是一个递归引用，用于表示多级关系
    CHILDREN_DATA_CLASS: Type[OneToManyBase] = None

    def __init__(self, *args, **kwargs):
        """
        初始化OneToManyBase类的实例。

        :param args: 传递给表类的参数，用于初始化子表类实例。
        :param kwargs: 传递给表类的关键字参数，用于初始化子表类实例。
        """
        # 初始化表实例
        if self.CURRENT_TABLE_CLASS is None:
            raise RuntimeError("CURRENT_TABLE_CLASS is not set")
        self.item = self.CURRENT_TABLE_CLASS(*args, **kwargs)
        # 定义父级数据关系实例
        self.parent: Optional[OneToManyBase] = None
        # 定义子级数据关系列表
        self.children: list[OneToManyBase] = []
        # 定义数据库会话实例
        self._session: Optional[Session] = None

    @property
    def children_length(self):
        return len(self.children)

    @property
    def session(self):
        if self._session is None:
            raise RuntimeError("Session is not set")
        return self._session

    @session.setter
    def session(self, session: Session):
        """
        设置所有项目的会话。

        此方法用于为属于当前实例的所有项目设置新的会话。它遍历所有项目并将新会话应用到每个项目上。

        :param session: 要设置的新会话对象。
        :return: 无返回值。
        """
        for item in self.get_all_items():
            item._session = session

    def child_entries(self) -> Optional[list[Base]]:
        """
        获取当前一对多关系中的子表项。

        此方法用于显式地指定数据库表对象 `self.item` 中的子表项属性，以在基类中实现通用的方法。

        Returns:
            object: 子表项列表。

        """
        pass

    def add_child(self, *args, **kwargs):
        """
        添加一个子表类实例作为当前实例的子元素。

        :param args: 传递给表类的参数，用于初始化子表类实例。
        :param kwargs: 传递给表类的关键字参数，用于初始化子表类实例。
        :return:
        """
        # 使用提供的参数和关键字参数创建一个子表类实例
        child = self.CHILDREN_DATA_CLASS(*args, **kwargs)
        # 设置新创建的子表类实例的父元素为当前实例
        child.parent = self
        child.session = self.session
        self.children.append(child)

    def get_all_items(self):
        """
        获取所有项目及其子项目的生成器方法

        该方法用于遍历当前项目及其所有子项目，生成一个包含自身及其所有子项目的序列。
        它通过递归方式深入到项目树的每一层，确保所有项目都被访问和返回。

        :return: 返回一个生成器，产出当前项目及其所有子项目
        """
        yield self  # 首先返回当前项目自身
        # 遍历所有子项，递归获取子项及其子项
        for child in self.children:
            yield from child.get_all_items()  # 再通过递归返回子项的所有子项

    def load_child_datas_from_database(self) -> None:
        """
        从数据库加载数据。

        该方法查询数据库中的子项并建立与当前项的关联。它还负责递归地为每个子项加载数据，
        但设置了防护以避免无限递归。

        :return: None
        """
        if self.CHILDREN_TABLE_CLASS is None:
            return
        try:
            # 查询数据库中所有子类项并建立关联
            if self.child_entries() is None:
                raise RuntimeError(
                    "child_entries is not set .Override the child_entries() method in the class to return the "
                    "items associated with subclasses in the table mapping object.")
            for child in self.child_entries():
                child_data = self.CHILDREN_DATA_CLASS()
                child_data.parent = self
                child_data.item = child
                child_data.session = self.session
                self.children.append(child_data)
            for child in self.children:
                # 避免无限递归
                if child.CHILDREN_TABLE_CLASS != self.CHILDREN_TABLE_CLASS:
                    child.load_child_datas_from_database()
        except Exception as e:
            print(f"Error loading data from database: {e}")

    def delete_current_datas(self):
        """
        删除当前所有的数据项。

        此方法通过获取所有数据项，然后使用数据库会话删除每个数据项并提交更改。
        """
        # 遍历所有数据项，逐个删除并提交
        for item in self.get_all_items():
            self.session.delete(item.item)
            self.session.commit()

    @classmethod
    def get_item_from_database(cls, session: Session, **kwargs) -> Optional[OneToManyBase]:
        """
        从数据库中根据名称获取项目。

        :param session: SQLAlchemy Session 对象
        :param kwargs: 过滤条件，用于 filter_by 方法。可以接受模型类中定义的任意属性作为关键字参数，并且可以组合多个条件进行过滤。
        :return: 匹配的项目对象，如果没有找到则返回 None
        """
        try:
            # 根据过滤条件从数据库中查询第一个匹配的记录
            result: cls = session.query(cls.CURRENT_TABLE_CLASS).filter_by(**kwargs).first()
            if result:
                result_ = cls()
                result_.item = result
                # 加载从数据库的子数据
                result_.load_child_datas_from_database()
                result_.session = session
                return result_
            else:
                raise RuntimeError(f"No item found in database under condition {str(kwargs)}")
        except Exception as e:
            # 处理数据库查询过程中的异常
            print(f"Error occurred during database query: {e}")
            return None

    @classmethod
    def clear_all_table_data(cls, session: Session):
        """
        删除当前表中的所有数据。

        :param session: 数据库会话对象，用于执行数据库操作。
        :return: 无返回值。
        """
        try:
            # # 如果存在子类，则递归调用子类的方法
            if cls.CHILDREN_DATA_CLASS is not None:
                cls.CHILDREN_DATA_CLASS.clear_all_table_data(session)
            # 删除表中的所有记录
            session.query(cls.CURRENT_TABLE_CLASS).delete()
            # 提交事务
            session.commit()
        except SQLAlchemyError as e:
            # 如果发生异常，回滚事务
            session.rollback()
            # 记录异常信息
            print(f"Error occurred while clearing table data: {e}")
        except Exception as e:
            # 如果发生异常，回滚事务
            session.rollback()
            # 打印异常信息
            print(f"Unexpected error occurred while clearing table data: {e}")

    def save_to_database(self) -> None:
        """
        将当前对象的所有子项保存到数据库。

        该方法首先从对象中获取所有子项，然后将这些子项添加到数据库会话中，
        并尝试提交事务。如果在提交过程中遇到完整性错误（例如唯一约束冲突），
        则会回滚事务并打印错误信息。如果遇到其他类型的异常，也会回滚事务并打印异常信息。

        :return: 无返回值
        """
        try:
            # 获取所有子项
            items = list(self.get_all_items())
            items = [item.item for item in items]
            # 添加所有子项到会话
            self.session.add_all(items)
            # 提交事务
            self.session.commit()
        except IntegrityError as e:
            # 回滚事务
            self.session.rollback()
            # 打印异常信息
            print(f"IntegrityError occurred: {e}")
        except Exception as e:
            # 回滚事务
            self.session.rollback()
            # 打印异常信息
            print(f"Error occurred during commit: {e}")


class GrandChildrenData(OneToManyBase):
    CURRENT_TABLE_CLASS = GrandChildrenTable


class ChildrenData(OneToManyBase):
    CURRENT_TABLE_CLASS = ChildrenTable
    CHILDREN_TABLE_CLASS = GrandChildrenTable
    CHILDREN_DATA_CLASS = GrandChildrenData

    def child_entries(self):
        return self.item.grandchildren


class ParentData(OneToManyBase):
    CURRENT_TABLE_CLASS = ParentTable
    CHILDREN_TABLE_CLASS = ChildrenTable
    CHILDREN_DATA_CLASS = ChildrenData

    def child_entries(self):
        return self.item.children


if __name__ == '__main__':
    pass
    # 创建数据库引擎
    engine = create_engine('sqlite:///example.db', echo=True)
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    # 删除表数据
    # ParentData.clear_all_table_data(Session())

    # 生成保存表数据
    parents = [ParentData(name=f"parent{i}") for i in range(5)]
    for parent in parents:
        parent.children = [parent.CHILDREN_DATA_CLASS(name=f"child{i}", parent=parent.item) for i in range(10)]
        for child in parent.children:
            child.children = [child.CHILDREN_DATA_CLASS(name=f"grandchild{i}", child=child.item) for i in
                              range(10)]
    for parent in parents:
        parent.session = Session()
        parent.save_to_database()

    # # 查询打印指定数据项
    # parent = ParentData.get_item_from_database(Session(), name="parent3")
    # parent_str = f"parent_id:'{parent.item.id}',parent_name:'{parent.item.name}'\t"
    # print(parent_str)
    # for children in parent.children:
    #     children_str = f"children_id:'{children.item.id}',children_name:'{children.item.name}'\t"
    #     print(parent_str + children_str)
    #     for grandchild in children.children:
    #         grandchild_str = f"grandchild_id:'{grandchild.item.id}',grandchild_name:'{grandchild.item.name}'\t"
    #         print(parent_str + children_str + grandchild_str)
    # # 删除查询到的指定数据
    # parent.delete_current_datas()
